package com.codeplex.tdmg4mc;

import java.util.List;

import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;

public class EntityGearHook extends Entity
{
	public enum HoldingType
	{
		None(0),
		Block(1),
		Entity(2);
		private int index;
		private HoldingType(int i)
		{
			index=i;
		}
		public int getIndex()
		{
			return index;
		}
		public static HoldingType getHoldingType(int i)
		{
			switch(i)
			{
			case 1:
				return HoldingType.Block;
			case 2:
				return HoldingType.Entity;
			default:
				return HoldingType.None;
			}
		}
	}
	public static final double FLY_VELOCITY=10;
	public static final double REEL_VELOCITY=1;
	public static final double MAX_LENGTH=150;
	public static final double MIN_LENGTH=5;
	public static final double GROUND_MAX=100;
	private EntityPlayer owner;
	private ItemGear armor;
	private boolean left;
	private HoldingType holdingType;
	private int holdingX;
	private int holdingY;
	private int holdingZ;
	private Entity holdingEntity;
	private double length;
	private boolean reeling;
	private boolean reeled;
	private int onGroundCount;

	public EntityGearHook(World world)
	{
		super(world);
        ignoreFrustumCheck=true;
	}

	public EntityGearHook(World world, EntityPlayer player, boolean isLeft)
	{
		this(world, player, isLeft, player.getLookVec());
	}
	
	public EntityGearHook(World world, EntityPlayer player, boolean isLeft, Vec3 direction)
	{
		this(world);
		if(player.getCurrentArmor(1)==null||player.getCurrentArmor(1).getItem()!=TDMG.legs)
		{
			setDead();
			return;
		}
		owner=player;
		armor=(ItemGear)player.getCurrentArmor(1).getItem();
		left=isLeft;
		holdingType=HoldingType.None;
		onGroundCount=0;
		double a=left?0.2:-0.2;
		float yaw=rotationYaw/180.0f*(float)Math.PI;
		setLocationAndAngles(player.posX+MathHelper.cos(yaw)*a, player.posY-1, player.posZ+MathHelper.sin(yaw)*a, (float)(Math.atan2(direction.xCoord, direction.zCoord)/Math.PI*180.0),
				(float)(Math.atan2(direction.yCoord, Math.sqrt(direction.xCoord*direction.xCoord+direction.zCoord*direction.zCoord))/Math.PI*180.0));
		setVelocity(direction.xCoord*FLY_VELOCITY, direction.yCoord*FLY_VELOCITY, direction.zCoord*FLY_VELOCITY);
        worldObj.playSound(owner.posX, owner.posY, owner.posZ, "random.bow", 1f, 1f, false);
        worldObj.spawnParticle("explode", posX, posY, posZ, 0.0, 0.0, 0.0);
	}
	
	@Override
	public void onUpdate()
	{
		super.onUpdate();
		if(TDMG.isClient==worldObj.isRemote)
		{
			if(armor==null||(left&&armor.hookLeft!=this)||(!left&&armor.hookRight!=this)||
					owner==null||owner.getCurrentArmor(1)==null||owner.getCurrentArmor(1).getItem()!=TDMG.legs||onGroundCount>GROUND_MAX)
			{
				setDead();
				return;
			}
			if(holdingType==HoldingType.None)
			{
				posX+=motionX;
				posY+=motionY;
				posZ+=motionZ;
				length=getDistanceToEntity(owner);
				if(length>MAX_LENGTH)
				{
					setDead();
					return;
				}
				List entities=worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0, 1.0, 1.0));
				double d=Double.MAX_VALUE;
				double p=Double.MAX_VALUE;
				for(Object o:entities)
				{
					Entity e=(Entity)o;
					if(e==owner||e==armor.hookLeft||e==armor.hookRight) continue;
					double ep=e.getDistance(prevPosX, prevPosY, prevPosZ);
					double ed=getDistanceToEntity(e)+ep;
					if((d>ed||(d==ed&&p>ep))&&owner.getDistanceToEntity(e)>1.0)
					{
						holdingType=HoldingType.Entity;
						holdingEntity=e;
						d=ed;
						p=ep;
					}
				}
				MovingObjectPosition mop=worldObj.rayTraceBlocks(worldObj.getWorldVec3Pool().getVecFromPool(prevPosX, prevPosY, prevPosZ),
						worldObj.getWorldVec3Pool().getVecFromPool(posX, posY, posZ), false);
				if(mop!=null&&mop.typeOfHit==MovingObjectType.BLOCK)
				{
					holdingX=mop.blockX;
					holdingY=mop.blockY;
					holdingZ=mop.blockZ;
					if(holdingType==HoldingType.Entity)
					{
						double dx=posX-(holdingX+0.5);
						double dy=posY-(holdingY+0.5);
						double dz=posZ-(holdingZ+0.5);
						double px=prevPosX-(holdingX+0.5);
						double py=prevPosY-(holdingY+0.5);
						double pz=prevPosZ-(holdingZ+0.5);
						if(d>Math.sqrt(dx*dx+dy*dy+dz*dz)+Math.sqrt(px*px+py*py+pz*pz)) holdingType=HoldingType.Block;
					}
					else holdingType=HoldingType.Block;
				}
				if(holdingType==HoldingType.Entity)
					holdingEntity.attackEntityFrom(DamageSource.causeThrownDamage(this, owner), 2);
			}
			else if(left)
			{
				if(TDMGKeyInput.left)
				{
					TDMGKeyInput.left=false;
					if(reeled)
					{
						setDead();
						return;
					}
					reeling=true;
				}
				else if(reeling&&!TDMGKeyInput.leftPress)
				{
					reeling=false;
					reeled=true;
				}
			}
			else
			{
				if(TDMGKeyInput.right)
				{
					TDMGKeyInput.right=false;
					if(reeled)
					{
						setDead();
						return;
					}
					reeling=true;
				}
				else if(reeling&&!TDMGKeyInput.rightPress)
				{
					reeling=false;
					reeled=true;
				}
			}
			switch(holdingType)
			{
			case None:
				setPosition(posX, posY, posZ);
				setVelocity(motionX, motionY, motionZ);
				break;
			case Block:
				{
					if(!worldObj.blockExists(holdingX, holdingY, holdingZ))
					{
						setDead();
						return;
					}
					setPosition(holdingX+0.5, holdingY+0.5, holdingZ+0.5);
					setVelocity(0, 0, 0);
					if(reeling)
					{
						length-=REEL_VELOCITY;
				        worldObj.playSound(owner.posX, owner.posY, owner.posZ, "random.bow", 0.5f, 1f, false);
						double a=left?0.2:-0.2;
						float yaw=owner.rotationYaw/180.0f*(float)Math.PI;
				        worldObj.spawnParticle("explode", owner.posX+MathHelper.cos(yaw)*a, owner.posY-1, owner.posZ+MathHelper.sin(yaw)*a, 0.0, 0.0, 0.0);
					}				
			        if(length<MIN_LENGTH) length=MIN_LENGTH;
				}
				break;
			case Entity:
				{
					if(holdingEntity==null||holdingEntity.isDead)
					{
						setDead();
						return;
					}
					if(reeling)
					{
						length-=REEL_VELOCITY;
				        worldObj.playSound(owner.posX, owner.posY, owner.posZ, "random.bow", 0.5f, 1f, false);
						double a=left?0.2:-0.2;
						float yaw=owner.rotationYaw/180.0f*(float)Math.PI;
				        worldObj.spawnParticle("explode", owner.posX+MathHelper.cos(yaw)*a, owner.posY-1, owner.posZ+MathHelper.sin(yaw)*a, 0.0, 0.0, 0.0);
					}
					if(length<MIN_LENGTH) length=MIN_LENGTH;
					double d=owner.getDistanceToEntity(holdingEntity);
					if(d>length)
					{
						double a=(d-length)/d;
						double dx=owner.posX-holdingEntity.posX;
						double dy=owner.posY-holdingEntity.posY;
						double dz=owner.posZ-holdingEntity.posZ;
						holdingEntity.setPosition(holdingEntity.posX+dx*a, holdingEntity.posY+dy*a, holdingEntity.posZ+dz*a);
					}
					setPosition(holdingEntity.posX, holdingEntity.posY, holdingEntity.posZ);
					setVelocity(holdingEntity.motionX, holdingEntity.motionY, holdingEntity.motionZ);
				}
				break;
			}
			if(owner.onGround)
			{
				if(onGroundCount<=GROUND_MAX) onGroundCount++;
			}
			else onGroundCount=0;
		}
	}
	
	public EntityPlayer getOwner()
	{
		return owner;
	}
	
	public ItemGear getArmor()
	{
		return armor;
	}
	
	public boolean isLeft()
	{
		return left;
	}
	
	public double getLength()
	{
		return length;
	}
	
	public boolean isHolding()
	{
		return holdingType!=HoldingType.None;
	}

	@Override
	protected void entityInit()
	{
	}

	@Override
	protected void readEntityFromNBT(NBTTagCompound nbt)
	{
		if(!nbt.hasKey("owner"))
		{
			setDead();
			return;
		}
		Entity o=worldObj.getEntityByID(nbt.getInteger("owner"));
		if(o==null||o.isDead||o instanceof EntityPlayer)
		{
			setDead();
			return;
		}
		owner=(EntityPlayer)o;
		if(owner.getCurrentArmor(1)==null||owner.getCurrentArmor(1).getItem()!=TDMG.legs)
		{
			setDead();
			return;
		}
		armor=(ItemGear)owner.getCurrentArmor(1).getItem();
		left=nbt.getBoolean("left");
		if(left) armor.hookLeft=this;
		else armor.hookRight=this;
		holdingType=HoldingType.getHoldingType(nbt.getInteger("holdingType"));
		holdingX=nbt.getInteger("holdingX");
		holdingY=nbt.getInteger("holdingY");
		holdingZ=nbt.getInteger("holdingZ");
		length=nbt.getDouble("length");
		reeling=nbt.getBoolean("reeling");
		reeled=nbt.getBoolean("reeled");
		if(holdingType==HoldingType.Entity)
		{
			if(!nbt.hasKey("holdingEntity"))
			{
				setDead();
				return;
			}
			holdingEntity=worldObj.getEntityByID(nbt.getInteger("holdingEntity"));
		}
	}

	@Override
	protected void writeEntityToNBT(NBTTagCompound nbt)
	{
		nbt.setInteger("owner", owner.getEntityId());
		nbt.setBoolean("left", left);
		nbt.setInteger("holdingType", holdingType.getIndex());
		nbt.setInteger("holdingX", holdingX);
		nbt.setInteger("holdingY", holdingY);
		nbt.setInteger("holdingZ", holdingZ);
		if(holdingEntity!=null)nbt.setInteger("holdingEntity", holdingEntity.getEntityId());
		nbt.setDouble("length", length);
		nbt.setBoolean("reeling", reeling);
		nbt.setBoolean("reeled", reeled);
	}

}
